import java.awt.*;
import java.awt.image.*;
import java.text.NumberFormat;
import java.text.DecimalFormat;
import java.util.Locale;
import java.text.ParseException;

public class DrawableAxis extends Component {
	static final boolean VERTICAL = true;
	static final boolean HORIZONTAL = false;
    static final int pMajorTickLength = 3;
    static final int pMinorTickLength = 1;
	int iLength;
    int maxWidth;
	Font f;
	FontMetrics fm;
	int fontAscent, fontHeight;
	int xnow, ynow;
	boolean bOrientation;
	double axisStart, axisFinish;
	double increment;
	DecimalFormat decform;
	Font numberFont;
    boolean bHaveLabel;
    String sLabel;
    Image iLabel;
    Font labelFont;

	// Should just be a length and horizontal or vertical.
	public DrawableAxis(int iLength, boolean bVertical, double start, double finish,
			Font f) {
		this.iLength = iLength;
		this.bOrientation = bVertical;
		axisStart = start;
		axisFinish = finish;
		NumberFormat numform = NumberFormat.getInstance(Locale.US);
		if (numform instanceof DecimalFormat) {
			decform = (DecimalFormat) numform;
			decform.applyPattern("0.###");
		} else decform = null;
		numberFont = f;
		increment = chooseIncrement(axisFinish-axisStart,iLength,bVertical);
		bHaveLabel = false;
	}

    public void setLabel(String s1, Font lf ) {
        sLabel = s1;
        labelFont = lf;
        bHaveLabel = true;
    }

    public void setLabel(Image i1) {
        iLabel = i1;
        bHaveLabel = true;
    }

	public void draw(Graphics g, int x, int y) {
		xnow = x; ynow = y;
        maxWidth = 0;
		if (bOrientation==VERTICAL) {
			g.drawLine(xnow,ynow,xnow,ynow-iLength);
		} else {
			g.drawLine(xnow,ynow,xnow+iLength,ynow);
		}
		fm = Toolkit.getDefaultToolkit().getFontMetrics(numberFont);
		fontAscent = fm.getAscent();
		fontHeight = fm.getHeight();
		g.setFont(numberFont);
		makeTicks(g,increment,pMajorTickLength,true);
		makeTicks(g,increment/5,pMinorTickLength,false);
        if (bHaveLabel) drawLabel(g);
	}

    private void drawLabel(Graphics g) {
        int width;
        if (bOrientation==HORIZONTAL) {
            FontMetrics lfm = Toolkit.getDefaultToolkit().getFontMetrics(labelFont);
            width = lfm.stringWidth(sLabel);
            // fontHeight is the height of the tick font.
            g.setFont(labelFont);
            g.drawString(sLabel,xnow+(iLength-width)/2,ynow+pMajorTickLength+fontHeight+
                lfm.getHeight());
        } else {
            width = iLabel.getWidth(null);
            int height = iLabel.getHeight(null);
            //System.out.println("Label width "+width+" height "+height);
            g.drawImage(iLabel,xnow-pMajorTickLength-maxWidth-width-3,
                ynow-(iLength+height)/2,null);
        }
    }

	private void makeTicks(Graphics g,double spacing, int ticklength, boolean numbered) {
		int iMark;
		double axisMark = axisStart;
		while (axisMark < axisFinish) {
			iMark = (int)(iLength*(axisMark-axisStart)/(axisFinish-axisStart));
			String s1;
			if (decform == null) s1 = Double.toString(axisMark);
			else s1 = decform.format(axisMark);
			if (bOrientation==VERTICAL) {
				g.drawLine(xnow,ynow-iMark,xnow-ticklength,ynow-iMark);
				if (numbered) textToLeft(s1,g,xnow-ticklength,ynow-iMark);
			} else {
				g.drawLine(xnow+iMark,ynow,xnow+iMark,ynow+ticklength);
				if (numbered) textBelow(s1,g,xnow+iMark,ynow+ticklength);
			}
			axisMark += spacing;
		}
	}

	protected void textBelow(String s1, Graphics g, int x, int y) {
		int width = fm.stringWidth(s1);
		g.drawString(s1,x-width/2,y+fontHeight);
	}

	protected void textToLeft(String s1, Graphics g, int x, int y) {
		int width = fm.stringWidth(s1);
		if (width>maxWidth) maxWidth = width;
		g.drawString(s1,x-width-1,y-fontHeight/2+fontAscent);
	}

	// A method to help axes choose their increments.
	// Use increments of 10, 20, 25 or 50 in whatever power of ten is appropriate.
	private double chooseIncrement(double domain, int pixels, boolean bOrientation)
	{
		double bases[] = {1, 2, 2.5, 5};

		if (domain<0) return(0.0);
		FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(numberFont);
		int height = fm.getHeight();

		// estimate the increment to allow 30 pixels per label.
		double estimatedIncrement = domain*30/pixels;
		// Use that estimate to get an order of magnitude to look for the real label.
		int order = (int) Math.floor(Math.log(estimatedIncrement)/Math.log(10.0));

		int sizeNeeded;
		double numLabels = 5, lastLabel = 1.0;
		// Loop through powers of ten.
		while (order<5) {
			// Loop through acceptable increments.
			for (int i = 0; i<4; i++) {
				double testIncrement = Math.pow(10,order)*bases[i];
				// how many pixels per label this increment leaves.
				int widthAvailable = (int) (pixels*testIncrement/domain);
				if (bOrientation==DrawableAxis.HORIZONTAL) {
					// how many labels we need to draw.
					numLabels = Math.floor(domain/testIncrement);
					// find the last label b/c it is likely the largest.
					lastLabel = numLabels*testIncrement;
					// Then see how wide it is.
					sizeNeeded= fm.stringWidth(Double.toString(lastLabel));
				} else sizeNeeded = height;
				// If the label width (or height) is larger than a percentage of the
				// available width per label, than keep looking.  If the label width
				// is small enough, then return.
				if (sizeNeeded*1.4< widthAvailable) return testIncrement;
			}
			order += 1;
		}
		return(20.0);
	}
}
